// <editor-fold defaultstate="collapsed" desc="GNU GPLv3 Header">
/*
 * File:   WaveFormatPCMByte.java
 *
 * Copyright (C) 2013 Robert Antoni Buj Gelonch <rbuj@uoc.edu>
 * Copyright (C) 2013 David Megias Jimenez <dmegias@uoc.edu>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
// </editor-fold>
package uoc.libwave;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

/**
 * Class for storing the PCM U8 WAVE file format and, for reading and writing
 * its content in Data I/O Streams.
 *
 * @author Robert Antoni Buj Gelonch <rbuj@uoc.edu>
 * @version 1.0
 * @since 2013-08-30
 */
final public class WaveFormatPCMByte extends WaveFormatPCM<Byte> {

    /**
     * WAVE Data Chunk
     */
    ChunkDataPCMByte chunkData;

    /**
     *
     * @param strFilePath
     * @throws FileNotFoundException
     * @throws IOException
     *
     * @since 2013-09-01
     */
    public WaveFormatPCMByte(String strFilePath) throws FileNotFoundException,
            IOException {
        FileInputStream fis = new FileInputStream(strFilePath);
        DataInputStream dis = new DataInputStream(fis);
        WaveFile wavefile = new WaveFile();
        // Header Chunk
        String headerChunkID = wavefile.readCkID(dis);
        if (!headerChunkID.equals("RIFF")){
            throw new UnsupportedOperationException("headerChunkID != 'RIFF'");
        }
        int headerChunkSize = wavefile.readCkSize(dis);
        String headerRiffType = wavefile.readRiffType(dis);
        if (!headerRiffType.equals("WAVE")){
            throw new UnsupportedOperationException("headerRiffType != 'WAVE'");
        }
        headerWaveFile = new ChunkHeader(headerChunkID, headerChunkSize,
                headerRiffType);
        // Format Chunk
        String formatChunkID = wavefile.readCkID(dis);
        if (!formatChunkID.equals("fmt ")){
            throw new UnsupportedOperationException("formatChunkID != 'fmt '");
        }
        int formatChunkSize = wavefile.readCkSize(dis);
        short wFormatTag = wavefile.readWFormatTag(dis);
        if (wFormatTag != WAVE_FORMAT_PCM) {
            throw new UnsupportedOperationException("wFormatTag != WAVE_FORMAT_PCM");
        }
        short wChannels = wavefile.readWChannels(dis);
        int dwSamplesPerSec = wavefile.readDWSamplesPerSec(dis);
        int dwAvgBytesPerSec = wavefile.readDWAvgBytesPerSec(dis);
        short wBlockAlign = wavefile.readWBlockAlign(dis);
        short wBitsPerSample = wavefile.readWBitsPerSample(dis);
        if (wBitsPerSample > 8) {
            throw new UnsupportedOperationException("wBitsPerSample > 8");
        }
        chunkFormatPCM = new ChunkFormatPCM(formatChunkID, formatChunkSize,
                wFormatTag, wChannels, dwSamplesPerSec, dwAvgBytesPerSec,
                wBlockAlign, wBitsPerSample);
        // Data Chunk
        String dataChunkID = wavefile.readCkID(dis);
        if (!dataChunkID.equals("data")){
            throw new UnsupportedOperationException("dataChunkID != 'data'");
        }
        int dataChunkSize = wavefile.readCkSize(dis);
        chunkData = new ChunkDataPCMByte(dataChunkID, dataChunkSize,
                wBitsPerSample, wChannels, dis);
        // Close I/O streams
        dis.close();
        fis.close();
    }

    /**
     *
     * @param HeaderChunkID
     * @param HeaderChunkSize
     * @param HeaderRiffType
     * @param FormatChunkID
     * @param FormatChunkSize
     * @param wFormatTag
     * @param wChannels
     * @param dwSamplesPerSec
     * @param dwAvgBytesPerSec
     * @param wBlockAlign
     * @param wBitsPerSample
     * @param DataChunkID
     * @param DataChunkSize
     * @param dis
     * @throws IOException
     *
     * @since 2013-08-30
     */
    public WaveFormatPCMByte(String HeaderChunkID, int HeaderChunkSize,
            String HeaderRiffType, String FormatChunkID, int FormatChunkSize,
            short wFormatTag, short wChannels, int dwSamplesPerSec,
            int dwAvgBytesPerSec, short wBlockAlign, short wBitsPerSample,
            String DataChunkID, int DataChunkSize, DataInputStream dis)
            throws IOException {
        super(HeaderChunkID, HeaderChunkSize, HeaderRiffType, FormatChunkID,
                FormatChunkSize, wFormatTag, wChannels, dwSamplesPerSec,
                dwAvgBytesPerSec, wBlockAlign, wBitsPerSample);
        chunkData = new ChunkDataPCMByte(DataChunkID, DataChunkSize,
                wBitsPerSample, wChannels, dis);
    }

    /**
     *
     * @return
     *
     * @since 2013-08-30
     */
    @Override
    public Byte getMaxSample() {
        return chunkData.getMaxSample();
    }

    /**
     *
     * @param wChannel
     * @param i
     * @return
     *
     * @since 2013-08-30
     */
    @Override
    public Byte getSample(short wChannel, int i) {
        return chunkData.getSample(wChannel, i);
    }

    /**
     *
     * @param wChannel
     * @param i
     * @param value
     *
     * @since 2013-08-30
     */
    @Override
    public void setSample(short wChannel, int i, Byte value) {
        chunkData.setSample(wChannel, i, value);
    }

    /**
     *
     * @param start
     * @param size
     * @param _Channel
     * @param lframe
     *
     * @since 2013-08-30
     */
    @Override
    public void getFrame(int start, int size, short _Channel, List<Byte> lframe) {
        lframe.clear();
        for (int i = 0; i < size; i++) {
            lframe.add(getSample(_Channel, start + i));
        }
    }

    /**
     *
     * @param start
     * @param lframe
     * @param _Channel
     *
     * @since 2013-08-30
     */
    @Override
    public void setFrame(int start, List<Byte> lframe, short _Channel) {
        for (int i = 0; i < lframe.size(); i++) {
            setSample(_Channel, start + i, lframe.get(i));
        }
    }

    /**
     *
     * @return
     *
     * @since 2013-08-30
     */
    @Override
    public int getNumSamples() {
        return chunkData.getNumSamples();
    }

    /**
     *
     * @param strDstFilePath
     * @throws IOException
     *
     * @since 2013-08-30
     */
    @Override
    public void saveToFile(String strDstFilePath) throws IOException {
        FileOutputStream fos = new FileOutputStream(strDstFilePath);
        DataOutputStream dos = new DataOutputStream(fos);
        headerWaveFile.save(dos);
        chunkFormatPCM.save(dos);
        chunkData.save(dos);
        dos.close();
        fos.close();
    }
}
